#include "client.h"

#include "log.h"
#include "joueur.h"

Client::Client() : QObject()
{
    socket = new QTcpSocket();
    connect(socket, SIGNAL(readyRead()), this, SLOT(donneesRecues()));
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
            SLOT(erreurSocket(QAbstractSocket::SocketError)));
    connect(socket, SIGNAL(connected()), this, SIGNAL(connecte()));
    connect(socket, SIGNAL(disconnected()), this, SIGNAL(deconnecte()));

    tailleMessage = 0;
}


void Client::connexion(QString host, int port)
{
    socket->abort();
    socket->connectToHost(host, port);
}

void Client::deconnexion()
{
    socket->abort();
}


void Client::envoyerDonnees(ActionNet action, QString donnee)
{
    QByteArray paquet;
    QDataStream out(&paquet, QIODevice::WriteOnly);

    /* Construction du paquet
     * Format:
     * [size][action][donnee]
     * [uint16][uint16][string]
     **/
    out << (quint16) 0;
    out << (quint16)action;
    out << donnee;
    out.device()->seek(0);
    out << (quint16) (paquet.size() - sizeof(quint16));

    LOG3(ODEBUG, "envoyerDonnees: %1 (taille %2), donnee %3",
             ANet::getNom(action), (paquet.size() - sizeof(quint16)), donnee);

    /* On envoie le paquet */
    LOG(ODEBUG, "socket : ");
    qDebug() << socket ;
    socket->write(paquet);
}


void Client::donneesRecues()
{
    QDataStream in(socket);

    if (tailleMessage == 0)
    {
        if (socket->bytesAvailable() < (int)sizeof(quint16))
             return;

        in >> tailleMessage;
    }

    if (socket->bytesAvailable() < tailleMessage)
        return;

    /* Reconstruction du paquet
     * Format:
     * [size][action][donnee]
     * [uint16][uint16][string]
     **/
    quint16 uaction;
    in >> uaction;
    ActionNet action = static_cast<ActionNet>(uaction);

    QString donnee;
    in >> donnee;

    LOG3(ODEBUG, "donneesRecues: %1 (%2), donnee %3",
             ANet::getNom(action), action, donnee);

    tailleMessage = 0;

    emit dispatch(action, donnee);

    /* Si il reste des donnée à traiter on rappel donneesRecues */
    if (socket->bytesAvailable() > 0 )
        donneesRecues();
}


void Client::erreurSocket(QAbstractSocket::SocketError erreur)
{
    QString msg;
    switch(erreur)
    {
        case QAbstractSocket::HostNotFoundError:
            msg = trUtf8("le serveur n'a pas pu être trouvé. Vérifiez l'IP et le port.");
            break;
        case QAbstractSocket::ConnectionRefusedError:
            msg = trUtf8("le serveur a refusé la connexion. Vérifiez si le serveur a bien été lancé. Vérifiez aussi l'IP et le port.");
            break;
        case QAbstractSocket::RemoteHostClosedError:
            msg = trUtf8("le serveur a coupé la connexion");
            break;
        default:
            msg = socket->errorString();
            break;
    }

    Log::out(OERREUR, metaObject()->className(), msg);
}

void Client::close()
{
    socket->abort();
}

void Client::dispatch(ActionNet action, QString donnee)
{

    switch(action){
        case ACT_JOUEUR_ID:
            actJoueurId(donnee);
            break;

        case ACT_JOUEUR_NEW_CON:
            actJoueurConnexion(donnee);
            break;

        case ACT_JOUEUR_DECO:
            actJoueurDeconnexion(donnee);
            break;

        case ACT_JOUEURS_LIST:
            actJoueursList(donnee);
            break;

        case ACT_MESSAGE:
            actMessage(donnee);
            break;

        case ACT_PAYS:
            actPaysList(donnee);
            break;

        case ACT_PION_ADD:
            actPionAdd(donnee);
            break;

        case ACT_PION_RM:
            actPionRm(donnee);
            break;

        case ACT_CHANGE_STATUT:
            actChangeStatut(donnee);
            break;

        case ACT_INITIALISE:
            actInitialise(donnee);
            break;

        case ACT_SET_JOUEUR:
            actSetJoueur(donnee);
            break;

        default:
            LOG(ODEBUG, "commande inconnue");
            break;
    }
}

void Client::actJoueurConnexion(QString donnee)
{
    QList<QString> param = donnee.split(";");
    int id = param.at(0).toInt();
    QString nom = param.at(1);

    LOG2(ODEBUG, "actJoueurConnexion n°%1 %2", id, nom);

    emit recvJoueurConnexion(new Joueur(id, nom));
}

void Client::actJoueurDeconnexion(QString donnee)
{
    int id = donnee.toInt();

    LOG1(ODEBUG, "actJoueurDeconnexion n°%1", id);

    emit recvJoueurDeconnexion(id);
}

void Client::actJoueursList(QString donnee)
{
    QList<Joueur *> joueurs;

    QList<QString> joueursStr = donnee.split(";");


    //LOG(ODEBUG, "actJoueurList");

    foreach(QString joueur, joueursStr){
        QList<QString> param = joueur.split(":");

        if ( param.count() != 2 )
            continue;

        int id = param.at(0).toInt();
        QString nom = param.at(1);

        joueurs.append(new Joueur(id, nom));

        //LOG2(ODEBUG, "actJoueurList n°%1 %2",id, nom);
    }


    emit recvJoueursList(joueurs);
}

void Client::actJoueurId(QString donnee)
{
    //LOG(ODEBUG, "actJoueurId");

    int id = donnee.toInt();
    emit recvJoueurId(id);
    //client->envoyerDonnees(ACT_JOUEURS_LIST, moi->getNom());
}

void Client::actMessage(QString donnee)
{
    QStringList param = donnee.split(":");

    if ( param.count() < 2 ) return; // peut-être 3 voir 4 ^^
    QString dest = param.at(0);
    LOG1(ODEBUG, "%1", donnee);
    if ( param.count() >= 4)
    {
        if ( param.at(3) == "ONOTICE")
        {
            LOG1(ONOTICE,"%1", param.at(2) );
        }
    }
    else
    {
        QString src = param.at(1);
        param.removeFirst();

        QString msg = param.join(":");

        //qDebug() << "TEST JOIN : SRC XX MSG XX DEST: " << src << "XX" << msg << "XX" << dest;
        LOG(ODEBUG,"Appelle recvMessage deuis actMesage");
        emit recvMessage(src, msg, dest);
    }
}

void Client::actPaysList(QString donnee)
{
    QHash<int, int> etatsjoueurs;

    QList<QString> coupleStrList = donnee.split(";");
    //LOG(ODEBUG, "actPaysList");

    foreach(QString coupleStr, coupleStrList){
        QList<QString> param = coupleStr.split(":");

        if ( param.count() != 2 )
            continue;

        int idetat = param.at(0).toInt();
        int idjoueurs = param.at(1).toInt();
        etatsjoueurs[idetat] = idjoueurs;

        //LOG(ODEBUG, "etat n°%1 avec joueur n°%2", idetat, idjoueurs);
    }


    emit recvPaysList(etatsjoueurs);
}

void Client::actPionAdd(QString donnee)
{
    int etatid = donnee.toInt();

    emit recvPionAdd(etatid);
}

void Client::actPionRm(QString donnee)
{
    int etatid = donnee.toInt();

    emit recvPionRm(etatid);
}

void Client::actChangeStatut(QString donnee)
{
    LOG1(ODEBUG,"donne %1",donnee);
    int etape = donnee.toInt() ;
    /* je ne pense pas que ce soit vraiment nécessaire de
      dire quel est le numéro de l'étape qu'on doit aborder puisqu'on peut le déduire
      au vu de l'état actuel des étapes mais comme il faut envoyer quelque chose
      autant faire simple.
      //*/


    emit recvChangeStatut(etape);

}

void Client::actInitialise(QString donnee)
{
    //LOG(ODEBUG, "actInitialise");

    emit recvInitialise(donnee);
    //client->envoyerDonnees(ACT_JOUEURS_LIST, moi->getNom());
}

void Client::actSetJoueur(QString donnee)
{
    //LOG(ODEBUG, "actSetJoueur");

    emit recvSetJoueur(donnee);
}


